﻿/**
* @fileoverview ChromeFrameDetect.js provides a set of utilities for managing
* the Chrome Frame detection and installation process.
* @author David Murdoch | June 11, 2010
*/

/**
* Original source from slightlyoff@google.com (Alex Russell)
* More Info: http://www.chromium.org/developers/how-tos/chrome-frame-getting-started
*/

/*globals ActiveXObject: true */

(
/**
* Scope closure
* @param {Window} window The window object
* @param {*=} undefined undefined
*/
function (window, undefined) {
    /** const */
    var NAME_SPACE = "CFDetect";

    if (window[NAME_SPACE]) {
        // been there, done that
        return;
    }

    // cache some nodes
    var document = window.document,
        html = document.documentElement,
        _children = html.childNodes,
        head = _children[0],
        body = _children[1];

    // we don't need _children anymore.
    _children = null;

    /**
    * Gets the height of the document
    * @return {number}
    */
    var getDocumentHeight = function () {
        return html.clientHeight || body.clientHeight;
    };

    /**
    * Gets the width of the document
    * @return {number}
    */
    var getDocumentWidth = function () {
        return html.clientWidth || body.clientWidth;
    };



    /** @const */
    var PREFIX = "GCF";
    /** @const */
    var BORDER = 1;
    /** @const */
    var PADDING = 8;
    /** @const */
    var MAX_ZINDEX = 2147483647;
    /** @const */
    var BACKGROUND_COLOR = "#999";
    /** @const */
    var BACKGROUND_OPACITY = "50"; // percent
    /** @const */
    var TITLE = "Install Google Chrome Frame";
    /** @const */
    var INSTALL_LINK_TEXT = "Install Chrome Frame";
    /** @const */
    var MARGIN = 10;

    // depending on doctype, document.documentElement.clientHeight might be 0; fall back to document.body.clientHeight if so
    /** @const */
    var DOCUMENT_HEIGHT = "(document.documentElement.clientHeight || document.body.clientHeight)";
    /** @const */
    var DOCUMENT_WIDTH = "(document.documentElement.clientWidth || document.body.clientWidth)";

    var _outer = (BORDER + PADDING) * 2,
        _margin = MARGIN * 2,
    // implement max width/height
        WIDTH_EXPRESSION = "Math.min(" + DOCUMENT_WIDTH + "-" + (_outer + _margin) + ", 830)",
        HEIGHT_EXPRESSION = "Math.min(" + DOCUMENT_HEIGHT + "-" + (_outer + 31 + _margin) + ", 644)";

    /**
    * Gets the width of the iframe
    * @return {number}
    */
    var getWidth = function () {
        return Math.min(getDocumentWidth() - _outer - _margin, 830);
    };

    /**
    * Gets the height of the iframe
    * @return {number}
    */
    var getHeight = function () {
        return Math.min(getDocumentHeight() - _outer - 31 - _margin, 644);
    };

    /** 
    * Holds the browser version for later use.
    */
    var browserVersion;

    /**
    * Holds the activeElement at the moment the iframeModal is created.
    */
    var activeElement = document;

    // we need the options cache to handle the after-close-install-link
    var optionsCache = {};

    /**
    * Always returns false. You can use this empty function when you wish to pass 
    * around a function that will do nothing (or return false).
    * @return {boolean}
    */
    var NOOP = function () {
        return false;
    };


    /**
    * Checks to find out if ChromeFrame is available as a plugin
    * @return {boolean}
    * @public
    */
    var isAvailable = function () {

        // Look for CF in the User Agent before trying the
        // more expensive ActiveXObject check
        var ua = navigator.userAgent.toLowerCase();
        if (ua.indexOf("chromeframe") !== -1) {
            return true;
        }

        // probably don't need to use window.ActiveXObject here
        // because we are using a try/catch statement anyway.
        // TODO: figure out if need to check for ActiveXObject or not
        if (window["ActiveXObject"] !== undefined) {
            try {
                var obj = new ActiveXObject("ChromeTab.ChromeFrame");
                if (obj) {
                    return true;
                }
            } catch (e) {
                // squelch
            }
        }
        return false;
    };

    /**
    * Returns an item based on DOM ID. Optionally a document may be provided to
    * specify the scope to search in. If a node is passed, it's returned as-is.
    * @param {string|Node} id The ID of the node to be located or a node
    * @param {Node=} node A document to search for id (optional)
    * @return {Node}
    */
    var byId = function (id, node) {
        return typeof id === "string" ? (node || document).getElementById(id) : id;
    };

    /**
    * Creates a style sheet in the document containing the passed rules.
    * IE only allows 31 StyleSheets so we don't want to add stylesheet willy-nilly
    * What we do is add one stylsheet then append new rules to that stylesheet.
    *     *******  Hopefully this stylesheet we add isn't the 31st.  *******
    *     The limit that could occur with this technique is IE 4095 selector rule
    *     limit per stylesheet.
    * @param {string} rules The CSS rules to add to the document.
    */
    var appendStyleSheet = (function () {
        var ss;
        return function (rules) {
            if (ss) {
                ss.styleSheet.cssText += rules;
            }
            else {
                ss = document.createElement("style");
                ss.setAttribute("type", "text/css");
                ss.styleSheet.cssText = rules;
                head.appendChild(ss);
            }
        };
    } ());

    /**
    * Injects style rules into the document to handle formatting of Chrome Frame
    * prompt. Multiple calls have no effect.
    */
    var injectCFStyleTag = function () {
        // TODO: optimize the CSS rules

        // Need to re-write.
        //        var rules = {};
        //        rules["#" + PREFIX + "_OverlayContent"] = {
        //            "position": "absolute",
        //            "margin-left": "expression( -(" + WIDTH_EXPRESSION + " / 2 + 9))",
        //            "margin-top": "expression( -(" + HEIGHT_EXPRESSION + " / 2 - document.body.scrollTop + 25) )",
        //            "left": "50%",
        //            "top": "50%",
        //            "border": BORDER + "px solid #3a5774",
        //            "background": "#c1d9ff",
        //            "padding": PADDING + "px",
        //            "z-index": MAX_ZINDEX
        //        };
        //        rules["#" + PREFIX + "_IframeHolder"] = {
        //            "position": "relative"
        //        };
        //        rules["#" + PREFIX + "_IframeHolder iframe"] = {
        //            "width": "expression(" + WIDTH_EXPRESSION + ")",
        //            "height": "expression(" + HEIGHT_EXPRESSION + ")",
        //            "border": "none",
        //            "position": "relative"
        //        };
        //        rules["#" + PREFIX + "_OverlayHeader, #" + PREFIX + "_OverlayHeader td"] = {
        //            "background": "#e0edfe"
        //        };
        //        rules["#" + PREFIX + "_OverlayTitle"] = {
        //            "color": "#000",
        //            "cursor": "default",
        //            "font": "normal normal bold 16px/normal Arial, sans-serif",
        //            "padding": "0 4px",
        //            "vertical-align": "middle"
        //        };
        //        rules["#" + PREFIX + "_OverlayClose"] = {
        //            "text-align": "right"
        //        };
        //        rules["#" + PREFIX + "_OverlayClose span"] = {
        //            "text-align": "center",
        //            "font-size": "1.5em",
        //            "font-weight": "bold"
        //        };
        //        rules["#" + PREFIX + "_OverlayClose div"] = {
        //            "width": "1px",
        //            "height": "1px",
        //            "overflow": "hidden",
        //            "background": "#e0edfe",
        //            "position": "absolute",
        //            "top": "-1px",
        //            "left": "-1px"
        //        };
        //        rules["#" + PREFIX + "_CloseButton"] = {
        //            "position": "relative",
        //            "font": "13px/13px Arial, sans-serif",
        //            "display": "block",
        //            "cursor": "default",
        //            "text-decoration": "none",
        //            "border": "0",
        //            "color": "#7a94bb",
        //            /* IE6 doesn't need float to do what we want */
        //            /* (and the double float-margin bug shows up */
        //            "float": browserVersion < 7 ? "auto" : "right",
        //            "width": "15px",
        //            "height": "15px",
        //            "padding": "4px",
        //            "font-weight": "bold",
        //            "margin": "4px"
        //        };
        //        rules["#" + PREFIX + "_CloseButton:hover"] = {
        //            "padding": "3px",
        //            "border": "solid 1px",
        //            "border-top-color": "#99ccff",
        //            "border-left-color": "#6699ee",
        //            "border-bottom-color": "#6699ee",
        //            "border-right-color": "#77aaff"
        //        };
        //        rules["." + PREFIX + "_CloseButtonActive, #" + PREFIX + "_CloseButton:focus"] = {
        //            "background": "#c4ddff",
        //            "outline": "0",
        //            "padding": "3px",
        //            "border": "solid 1px",
        //            "border-top-color": "#99ccff",
        //            "border-left-color": "#6699ee",
        //            "border-bottom-color": "#6699ee",
        //            "border-right-color": "#77aaff"
        //        };
        //        rules["#" + PREFIX + "_CloseButton span"] = {
        //            "display": "block",
        //            "border": "solid 1px #7a94bb",
        //            "text-align": "center",
        //            "background": "#fff",
        //            "line-height": "13px",
        //            "overflow": "hidden",
        //            "width": "13px",
        //            "height": "13px"
        //        };
        //        rules["#" + PREFIX + "_OverlayUnderlay"] = {
        //            "position": "absolute",
        //            "top": "0",
        //            "left": "0",
        //            "width": "100%",
        //            "height": "100%",
        //            "background": BACKGROUND_COLOR,
        //            "filter": "alpha(opacity=" + BACKGROUND_OPACITY + ")",
        //            "z-index": MAX_ZINDEX-1
        //        };
        //        rules["html." + PREFIX + ", body." + PREFIX] = {
        //            "height": "100%",
        //            "width": "100%",
        //            "overflow": "hidden"
        //        };
        //        rules["html." + PREFIX + " body." + PREFIX + " select"] = {
        //            "visibility": "hidden"
        //        };

        //        var selectorArray = [], ruleArray, selector, s, attr;
        //        for (s in rules) {
        //            ruleArray = [];
        //            selector = rules[s];
        //            for (attr in selector) {

        //                ruleArray.push(attr.toLowerCase() + ": " + (
        //                    selector[attr])
        //                        .replace(BACKGROUND_COLOR, "BACKGROUND_COLOR")
        //                        .replace("opacity=" + BACKGROUND_OPACITY, "opacity=\" + BACKGROUND_OPACITY + \"")
        //                        .replace(WIDTH_EXPRESSION, "\" + WIDTH_EXPRESSION + \"")
        //                        .replace(HEIGHT_EXPRESSION, "\" + HEIGHT_EXPRESSION + \"")
        //                    );
        //            }
        //            selectorArray.push(s.replace(PREFIX, "\" + PREFIX + \"") + " {\" + \n    \"" + ruleArray.sort().join(";\" + \n    \"") + "\" + \n\"}\" + \n\"");
        //        }
        //console.log(selectorArray.join(""));

        var rules = "#" + PREFIX + "_OverlayContent {" +
            "background: #c1d9ff;" +
            "border: 1px solid #3a5774;" +
            "left: 50%;" +
            "margin-left: expression(-(" + WIDTH_EXPRESSION + "/2+9));" +
            "margin-top: expression(-(" + HEIGHT_EXPRESSION + "/2+25-document.body.scrollTop));" +
            "padding: 8px;" +
            "position: absolute;" +
            "top: 50%;" +
            "z-index: " + MAX_ZINDEX +
        "}" +
        "#" + PREFIX + "_IframeHolder {" +
            "position: relative" +
        "}" +
        "#" + PREFIX + "_IframeHolder iframe {" +
            "height: expression(" + HEIGHT_EXPRESSION + ");" +
            "width: expression(" + WIDTH_EXPRESSION + ")" +
        "}" +
        "#" + PREFIX + "_OverlayHeader td {" +
            "background: #e0edfe" +
        "}" +
        "#" + PREFIX + "_OverlayTitle {" +
            "color: #000;" +
            "cursor: default;" +
            "font: normal normal bold 16px/normal Arial, sans-serif;" +
            "padding: 0 4px;" +
            "vertical-align: middle" +
        "}" +
        "#" + PREFIX + "_OverlayClose {" +
            "text-align: right" +
        "}" +
        "#" + PREFIX + "_CloseButton {" +
            "color: #7a94bb;" +
            "cursor: default;" +
            (browserVersion < 7 ? "display: block; width: 0;" : "float: right;") +
            "font-weight: bold;" +
            "font: normal bold 13px/13px Arial, sans-serif;" +
            "margin: 4px;" +
            "outline: 0;" +
            "padding: 4px;" +
            "position: relative;" +
            "text-align: center;" +
            "text-decoration: none" +
        "}" +
        "#" + PREFIX + "_CloseButton span {" +
            "background: #fff;" +
            "border: solid 1px #7a94bb;" +
            "display: block;" +
            "height: 13px;" +
            "font-size: 1.5em;" +
            "overflow: hidden;" +
            "width: 13px;" +
            "padding: 1px" +
        "}" +
        "#" + PREFIX + "_CloseButton div {" +
            "background: #e0edfe;" +
            "height: 1px;" +
            "left: -1px;" +
            "overflow: hidden;" +
            "position: absolute;" +
            "top: -1px;" +
            "width: 1px" +
        "}" +
        "#" + PREFIX + "_CloseButton:hover {" +
            "border: solid 1px;" +
            "border-color: #9cf #7af #69e #69e;" +
            "padding: 3px" +
        "}" +
        "." + PREFIX + "_CloseButtonActive, #GCF_CloseButton:focus {" +
            "background: #c4ddff;" +
        "}" +
        "#" + PREFIX + "_OverlayUnderlay {" +
            "background: " + BACKGROUND_COLOR + ";" +
            "filter: alpha(opacity=" + BACKGROUND_OPACITY + ");" +
            "height: 100%;" +
            "left: 0;" +
            "position: absolute;" +
            "top: 0;" +
            "width: 100%;" +
            "z-index: " + MAX_ZINDEX +
        "}" +
        "html." + PREFIX + ", body.GCF {" +
            "height: 100%;" +
            "overflow: hidden;" +
            "width: 100%" +
        "}" +
        "html." + PREFIX + " body.GCF select {" +
            "visibility: hidden" +
        "}";

        appendStyleSheet(rules);

        // make this function do nothing.
        injectCFStyleTag = NOOP;
    };

    /**
    * Installs a cookie to prevent our Chrome Frame Install prompt from 
    * being displayed.
    */
    var installCookie = function () {
        // Hide the dialog for a year (or until the cookie is deleted).
        var age = 365 * 24 * 60 * 60 * 1000;
        document.cookie = PREFIX + "_disableCheck=1;max-age=" + age + "; path=/";
    };
    /**
    * Deletes the cookie that prevents our Chrome Frame Install prompt
    * from being displayed.
    * @public
    */
    var uninstallCookie = function () {
        document.cookie = PREFIX + "_disableCheck=0; max-age=-1; path=/";
    };

    /**
    * Places a link in the corner of the screen to display the prompt
    */
    var installLink = function () {
        var a = byId(PREFIX + "_installLink");
        if (a) {
            a.style.display = "";
        }
        else {
            a = document.createElement("a");
            a.id = PREFIX + "_installLink";
            a.innerText = INSTALL_LINK_TEXT;
            // don't bother moving css here to injectCFStyleTag()
            // it won't work because that stylsheet might not be added.
            a.style.cssText = "position:absolute;right:4px;bottom:4px;z-index:" + MAX_ZINDEX;
            a.href = "javascript:void(0)";
            a.onclick = function () {
                uninstallCookie();
                check(optionsCache);
                // if we "this.parentNode.removeChild(this);"
                // IE7 will flicker. Hide it instead.
                // -- Also, removing the element from the DOM
                // without nullifying the event causes memory leaks in IE6-7
                // more info here: http://javascript.crockford.com/memory/leak.html
                a.style.display = "none";
                return false;
            };
            body.appendChild(a);
        }
    };

    /**
    * Trims text
    * @param {string} text The text to trim
    * @return {string}
    */
    var trim = (function () {
        // trim method from jQuery commit:
        // http://github.com/jquery/jquery/commit/141ad3c3e21e7734e67e37b5fb39782fe11b3c18 
        var trimLeft = /^\s+/,
            trimRight = /\s+$/;
        return function (text) {
            return text.replace(trimLeft, "").replace(trimRight, "");
        };
    } ());

    /**
    * Removes the Chrome Frame Detect className from an element
    * @param {Node} node The node to remove the className from
    */
    var removeClass = (function () {
        // removeClass logic from jQuery 1.4.2
        var rclass = /[\n\t]/g;
        return function (node) {
            var className = (" " + node.className + " ")
                .replace(rclass, " ")
                .replace(" " + PREFIX + " ", " ");
            node.className = trim(className);
        };
    } ());

    /**
    * Injects style rules to hide the "overlay" mode of the GCF prompt.
    * Multiple calls have no effect.
    */
    var closeOverlay = function () {
        // remove the overlay
        var content = byId(PREFIX + "_OverlayContent"),
            underlay = byId(PREFIX + "_OverlayUnderlay");
        if (content) { body.removeChild(content); }
        if (underlay) { body.removeChild(underlay); }

        /* we remove the className we added to the body and html elements */
        removeClass(body);
        removeClass(html);

        installCookie();

        installLink();

        // restore focus the activeElement
        activeElement && activeElement.focus();

        return false;
    };

    /**
    * Plucks properties from the passed arguments and sets them on the passed
    * DOM node
    * @param {Node} node The node to set properties on
    * @param {Object} options A map of user-specified configuration properties
    *    ("cssText", "className", "id", and "src").
    */
    var setProperties = function (node, options) {
        var className = options["className"] || "",
            id = options["id"] || "",
            cssText = options["cssText"] || "",
            src = options["src"] || "javascript:'<html></html>'";

        node.className = className;
        node.id = id;
        node.style.cssText = cssText;

        // Defaults to javascript:<html></html> so we don't crash.
        // This method avoids the the page uses both secure and insecure content
        // about:blank doesn't do this.
        node.src = src;

        /* update the body and html classes */
        body.className += " " + PREFIX;
        html.className += " " + PREFIX;
    };

    /**
    * Creates an iframe.
    * @param {Object} options A map of user-specified configuration properties
    *    ("cssText", "className", "id", and "src").
    * @return {Node}
    */
    var makeIframe = function (options) {
        var el = document.createElement("iframe");
        el.setAttribute("frameBorder", "0");
        el.setAttribute("border", "0");
        // IE6 will display a horizontal scrollbar
        // if the vertical bar is displayed unless
        // scolling === "yes"
        if (browserVersion < 7) {
            el.setAttribute("scrolling", "yes");
        }
        setProperties(el, options);
        return el;
    };


    /**
    * Adds an unadorned iframe into the page, taking arguments to customize it.
    * @param {Object} options A map of user-specified configuration properties
    *    ("cssText", "className", "id", "src", and "node").
    */
    var makeInlinePrompt = function (options) {
        var ifr = makeIframe(options),
            srcNode = byId(options["node"]);
        if (srcNode) {
            ifr.id = ifr.id || srcNode["id"] || "";
            // replace the user-supplied node with our iframe
            srcNode.parentNode.replaceChild(ifr, srcNode);
        }
        else {
            // default for "inline" mode:
            body.appendChild(ifr);
        }
    };

    /**
    * Adds a styled, closable iframe into the page with a background that
    * emulates a modal dialog.
    * @param {Object} options A map of user-specified properties to set
    * @return {Node} The Overlay node
    */
    var makeOverlayPrompt = function (options) {

        // Inject the default styles
        injectCFStyleTag();

        var underlay = document.createElement("div");
        underlay.id = PREFIX + "_OverlayUnderlay";
        var overlay = document.createElement("div");
        overlay.id = PREFIX + "_OverlayContent";
        // disable text selection on the container
        overlay.onselectstart = NOOP;
        overlay.innerHTML = "<table cellpadding=0 cellspacing=0>" +
                                "<tr id=" + PREFIX + "_OverlayHeader>" +
                                  "<td id=" + PREFIX + "_OverlayTitle>" + (options["title"] || TITLE) + "</td>" +
                                  "<td align=right id=" + PREFIX + "_OverlayClose>" +
                                    "<a href='javascript:void(0)' id=" + PREFIX + "_CloseButton onmousedown='this.className=\"" + PREFIX + "_CloseButtonActive\";' onmouseup='this.className=\"\";' onmouseleave='this.className=\"\";' title=Close>" +
                                        "<span>&times;</span>" +
                                        "<div></div>" +
        // mox-model issues required express here.
        // we should probably do this a better way.
                                        "<div style=left:expression(this.parentNode.offsetWidth-2);></div>" +
                                        "<div style=top:expression(this.parentNode.offsetWidth-2);left:expression(this.parentNode.offsetWidth-2);></div>" +
                                        "<div style=top:expression(this.parentNode.offsetWidth-2);></div>" +
                                    "</a>" +
                                  "</td>" +
                                "</tr>" +
                                "<tr>" +
                                  "<td colspan=2 id=" + PREFIX + "_IframeHolder></td>" +
                                "</tr>" +
                            "</table>";
        var frag = document.createDocumentFragment();
        frag.appendChild(underlay);
        frag.appendChild(overlay);

        byId(PREFIX + "_CloseButton", frag).onclick = closeOverlay;

        var ifr = makeIframe(options);
        byId(PREFIX + "_IframeHolder", frag).appendChild(ifr);

        body.appendChild(frag);

        // set the active element so we can refocus that element
        // if the "close" button is clicked.
        activeElement = document.activeElement;
        // blur the element because an input field's cursor
        // shines through our iframe
        activeElement.value !== undefined && activeElement.blur();

        return overlay;

    };

    /**
    * Checks to see if Chrome Frame is available, if not, prompts the user to
    * install. Once installation begins, a background timer starts,
    * checkinging for a successful install every 1/2 second. Upon detection of
    * successful installation, the current page is reloaded, or if a
    * "destination" parameter is passed, the page navigates there instead.
    * @param {Object} options A bag of configuration properties. Respected
    *    properties are: "mode", "url", "destination", "node", "onmissing",
    *    "preventPrompt", "oninstall", "preventInstallDetection", "cssText", 
    *    "className", "onafterappend", and "fallbackmode" (fallbackmode valid
    *    for "default" mode only [popup]).
    * @public
    */
    var check = function (options) {

        // get options situated first because
        // installLink() (called by closeOverlay()) relies on its existence.
        optionsCache = options = options || {};

        // if we've asked to install chrome before but they said no...
        if (document.cookie.indexOf(PREFIX + "_disableCheck=1") !== -1) {
            // we closeOverlay() here because closeOverlay also adds
            // a link to reinstall the overlay
            closeOverlay();
            return;
        }


        var ua = navigator.userAgent,
            ieRe = /MSIE (\S+); Windows NT/;

        browserVersion = !ieRe.test(ua) ? 0 : parseFloat(ieRe.exec(ua)[1]);

        // Bail on IE versions < 6 or OS worse than XP/2003 w/ ServicePack 2
        if (browserVersion < 6 && ua.indexOf("SV1") === -1) {
            // This browser/OS is not supported
            return;
        }


        if (!isAvailable()) {
            // fire the user-defined onmissng function if supplied.
            if (options["onmissing"]) {
                options["onmissing"]();
            }

            // use document.location instead of window.location because
            // document.location is intended for read-only access...
            // which is what we are doing.
            var location = document.location;

            // set the src for our iframe.
            options["src"] = options["url"] || (location.protocol === "https:" ? "https:" : "http:") + "//www.google.com/chromeframe";

            if (!options["preventPrompt"]) {
                var installPrompt;
                switch (options["mode"] || "") {
                    case "inline":
                        installPrompt = makeInlinePrompt(options);
                        break;
                    // new window...                                                                                                                                                                                                                  
                    case "window":
                        var w = getWidth(),
                            h = getHeight(),
                            left = (screen.width - w) / 2,
                            top = (screen.height - h) / 2;
                        installPrompt = window.open(options["src"], PREFIX + "_promptWindow", "resizable=1,scrollbars=1,toolbar=0,width=" + w + ",height=" + h + ",left=" + left + ",top=" + top);
                        if (installPrompt) {
                            installPrompt.focus();
                        }
                        // we've got a popup blocker!
                        else if (options["fallbackmode"]) {
                            if (typeof options["fallbackmode"] === "string") {
                                options["mode"] = options["fallbackmode"];
                                // prevent an infinite loop if the fallbackmode is "window"
                                options["fallbackmode"] = null;
                            }
                            else {
                                options = options["fallbackmode"];
                            }
                            check(options);
                            return;
                        }
                        // check to see if the popup window has been closed
                        var checkWindow = setInterval(function () {
                            if (!installPrompt || installPrompt.closed) {
                                clearInterval(checkWindow);
                                closeOverlay();
                            }
                        }, 300);
                        break;
                    default:
                        installPrompt = makeOverlayPrompt(options);
                        break;
                }

                // onafterappend lets the user manipulate the overlay container after
                // it is added to the page
                if (options["onafterappend"]) {
                    options["onafterappend"](installPrompt);
                }
            }

            if (!options["preventInstallDetection"]) {

                var installTimer = null,
                    isInstalled = function () {
                        if (isAvailable()) {
                            if (options["oninstall"]) {
                                options["oninstall"]();
                            }

                            clearInterval(installTimer);

                            // redirect to our desired destination...
                            window.location = options["destination"] || location;
                        }
                    };
                // Begin polling for install success.
                // Wait 5 seconds before checking if chrome frame has finished installing...
                setTimeout(function () {
                    // then poll every 1/2 second...
                    installTimer = setInterval(isInstalled, 500);
                }, 4500);

            }
        }
    };

    // expose CFDetect to the external scope. We've already checked to make
    // sure we're not going to blow an existing object away.
    window[NAME_SPACE] = {
        "check": check,
        "isAvailable": isAvailable,
        "uninstallCookie": uninstallCookie
    };

} (this));